﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Ordinaire;
using Ordinaire.Db;
using Ordinaire.Taxonomy.Data;

namespace Ordinaire.Taxonomy
{
    /// <summary>
    /// Represents a class responsible for managing tags data from SQL data source.
    /// </summary>
    public class TagDataManager : ReferenceDataManager<OContext>
    {
        /// <summary>
        /// Deletes all data with the specified item.
        /// </summary>
        /// <param name="item">the item which the tags are associated to</param>
        public void DeleteAllByEntity(Entity<int> item)
        {
            DeleteAllByEntity<Tags>(item);
        }

        /// <summary>
        /// Deletes all data with the specified item.
        /// </summary>
        /// <typeparam name="TTable">the table type to use in the data source</typeparam>
        /// <param name="item">the item which the tags are associated to</param>
        public void DeleteAllByEntity<TTable>(Entity<int> item)
            where TTable : Tags
        {
            item.ThrowIfNull<ArgumentNullException>("item");
            item.Id.ThrowIfNull<InvalidOperationException>(String.Format(ExceptionMessage.Invalid, "item's id"));

            OContext context = Context;
            List<Tags> data = Context
                .GetTable<TTable>()
                .Where(Tags.EntityIs(item))
                .ToList();
            context.Tags.DeleteAllOnSubmit(data);
            context.SubmitChanges();
        }

        /// <summary>
        /// Returns a value indicating whether both specified item and vocab id already exist in the data source.
        /// </summary>
        /// <param name="item">the entity</param>
        /// <param name="vocabId">the vocab id</param>
        /// <returns>true if data already exists; otherwise false</returns>
        public bool Exists(Entity<int> item, int vocabId)
        {
            return Exists<Tags>(item, vocabId);
        }

        /// <summary>
        /// Returns a value indicating whether both specified item and vocab id already exist in the data source.
        /// </summary>
        /// <typeparam name="TTable">the table type to use in the data source</typeparam>
        /// <param name="item">the entity</param>
        /// <param name="vocabId">the vocab id</param>
        /// <returns>true if data already exists; otherwise false</returns>
        public bool Exists<TTable>(Entity<int> item, int vocabId)
            where TTable : Tags
        {
            item.ThrowIfNull<ArgumentNullException>("item");
            item.Id.ThrowIfEmpty<InvalidOperationException>(String.Format(ExceptionMessage.Invalid, "item's id"));
            vocabId.ThrowIfEmpty<ArgumentException>(String.Format(ExceptionMessage.Invalid, "vocabId"));

            int noOfRows = Context
                .GetTable<TTable>()
                .Where(Tags.EntityIs(item))
                .Where(Tags.VocabIdIs(vocabId))
                .Count();

            return (noOfRows == 0) ? false : true;
        }

        /// <summary>
        /// Returns all tag data with the specified item/entity.
        /// </summary>
        /// <param name="item">the related item</param>
        /// <returns>list of tag data</returns>
        public List<Tags> GetAllByEntity(Entity<int> item)
        {
            item.ThrowIfNull<ArgumentNullException>("item");
            item.Id.ThrowIfNull<InvalidOperationException>(String.Format(ExceptionMessage.Invalid, "item's id"));

            return Context
                .Tags
                .Where(Tags.EntityIs(item))
                .ToList();
        }

        /// <summary>
        /// Returns all tag data with the specified vocab id.
        /// </summary>
        /// <param name="id">the vocab id</param>
        /// <returns>list of tag data</returns>
        public List<Tags> GetAllByVocabId(int id)
        {
            return GetAllByVocabId<Tags>(id);
        }

        /// <summary>
        /// Returns all tag data with the specified vocab id.
        /// </summary>
        /// <typeparam name="TTable">the table type to use in the data source</typeparam>
        /// <param name="id">the vocab id</param>
        /// <returns>list of tag data</returns>
        public List<TTable> GetAllByVocabId<TTable>(int id)
            where TTable : Tags
        {
            id.ThrowIfNullOrEmpty<ArgumentException>(String.Format(ExceptionMessage.IsNullOrEmpty, "keyword"));
            return GetAllByForeignId<TTable, Vocabs, int, string>(id);
        }

        /// <summary>
        /// Returns all tag data with the specified vocab id and entity type.
        /// </summary>
        /// <param name="id">the vocab id</param>
        /// <param name="type">the entity type</param>
        /// <returns>list of tag data</returns>
        public List<Tags> GetAllByVocabIdAndEntityType(int id, Type type)
        {
            return GetAllByVocabIdAndEntityType<Tags>(id, type);
        }

        /// <summary>
        /// Returns all tag data with the specified vocab id and entity type.
        /// </summary>
        /// <typeparam name="TTable">the table type to use in the data source</typeparam>
        /// <param name="id">the vocab id</param>
        /// <param name="type">the entity type</param>
        /// <returns>list of tag data</returns>
        public List<Tags> GetAllByVocabIdAndEntityType<TTable>(int id, Type type)
            where TTable : Tags
        {
            id.ThrowIfEmpty<ArgumentException>(String.Format(ExceptionMessage.IsNullOrEmpty, "keyword"));

            return Context
                .GetTable<TTable>()
                .Where(Tags.VocabIdIs(id))
                .Where(Tags.EntityIs(type))
                .ToList();
        }
    }
}
